home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / Book Chapters / 10 - Networking / NovelNetwar / gameplay.c < prev    next >
Text File  |  1995-05-12  |  29KB  |  1,310 lines

  1. //    This module implements all the actual gameplay related activity.
  2.  
  3.  
  4. #include "NovelNetwar.h"
  5. #include <QDOffscreen.h>
  6. #include <math.h>
  7. #include <Sound.h>
  8.  
  9.  
  10.  
  11. #define LOCALPLAYER        0
  12.  
  13. #define LEFTARROW        0x1C
  14. #define RIGHTARROW        0x1D
  15. #define UPARROW            0x1E
  16.  
  17. #define LEFTARROWKEYMAPMASK        0x0008
  18. #define RIGHTARROWKEYMAPMASK    0x0010
  19. #define UPARROWKEYMAPMASK        0x0040
  20. #define SPACEKEYMAPMASK            0x0200
  21.  
  22. #define NUMANGLES        (short) (72)
  23. #define REALANGLEDELTA    (double) (360.0 / NUMANGLES)
  24. #define MINANGLEINDEX    (short) 0
  25. #define MAXANGLEINDEX    (short) (MINANGLEINDEX + NUMANGLES)
  26.  
  27. #define NUMSTARS        50
  28. #define SUNRADIUS        15
  29. #define SIDELENGTH        10
  30.  
  31. #define MAXSPEED        7.5
  32. #define THRUST            0.25
  33. #define GRAVITY            -300.0
  34. //#define GRAVITY            0.0
  35.  
  36.  
  37. #define PHOTONLIFETIME    100
  38. #define PHOTONFIREDELAY    20L
  39.  
  40. #define MAXNUMCHANNELS    4
  41.  
  42. #define EXPLOSIONTTL    40
  43.  
  44.  
  45.  
  46. enum { IDLE, FINDINGOPPONENTS, PLAYING, EXPLODING };
  47.  
  48.  
  49. //    Lots of private variables-- the names make their purpose fairly clear (I hope).
  50.  
  51. //    For smoother action, make our local variables floating point
  52.  
  53. static double            myXCoord,myYCoord,myVX,myVY;
  54. static double            myPhotonXCoords[MAXNUMPHOTONS],myPhotonYCoords[MAXNUMPHOTONS];
  55. static double            myPhotonVXs[MAXNUMPHOTONS],myPhotonVYs[MAXNUMPHOTONS];
  56.  
  57. //    For speed, make the coordinates of other players integer
  58.  
  59. static short            playerXCoord[MAXNUMPLAYERS],playerYCoord[MAXNUMPLAYERS];
  60. static short            playerLastXCoord[MAXNUMPLAYERS],playerLastYCoord[MAXNUMPLAYERS];
  61. static short            playerAngleIndex[MAXNUMPLAYERS],playerLastAngleIndex[MAXNUMPLAYERS];
  62. static short            playerTempCurrentX[MAXNUMPLAYERS],playerTempCurrentY[MAXNUMPLAYERS],playerTempCurrentAngle[MAXNUMPLAYERS];
  63. static short            photonLastXCoord[MAXNUMPLAYERS][MAXNUMPHOTONS],photonLastYCoord[MAXNUMPLAYERS][MAXNUMPHOTONS];
  64. static short            photonXCoord[MAXNUMPLAYERS][MAXNUMPHOTONS],photonYCoord[MAXNUMPLAYERS][MAXNUMPHOTONS];
  65. static short            photonTimeToLive[MAXNUMPLAYERS][MAXNUMPHOTONS],photonLastTimeToLive[MAXNUMPLAYERS][MAXNUMPHOTONS];
  66. static short            playerStatus[MAXNUMPLAYERS];
  67. static short            photonIndex;
  68. static unsigned long    photonLastTickCount;
  69. static short            xVertexTable[NUMANGLES+1][3],yVertexTable[NUMANGLES+1][3];
  70. static double            vxTable[NUMANGLES+1],vyTable[NUMANGLES+1];
  71. static double            photonVXTable[NUMANGLES+1],photonVYTable[NUMANGLES+1];
  72. static short            midx,midy;
  73. static CWindowPtr        gameWPtr;
  74. static CWindowRecord    gameWRecord;
  75. static GWorldPtr        gameMasterHiddenGWorld,gameTempHiddenGWorld;
  76. static Rect                gameRect,sunRect;
  77. static int                gameNumPlayers;
  78. static AddrBlock        gamePlayerAddresses[MAXNUMPLAYERS];
  79. static char                sendBroadcastUpdate,dataBroadcastIsComplete,startExplosionSND;
  80. static Handle            gameMyPhotonSNDHandle,gameEnemyPhotonSNDHandle,gameExplosionSNDHandle;
  81. static SndChannelPtr    gameSndChannelPtr[MAXNUMCHANNELS],gameExplosionSndChannelPtr;
  82. static int                gameExplosionTimeToLive;
  83.  
  84.  
  85.  
  86.  
  87. static void DrawSunAndStars(void);
  88. static void ErasePhotons(void);
  89. static void DrawPhotons(void);
  90. static void BroadcastLocalPlayerData(void);
  91. static void EraseOffscreenShip(int whichShip);
  92. static void DrawOffscreenShip(int whichShip);
  93. static void ShowShip(int whichShip);
  94. static void PlayGameSound(Handle theSNDHandle);
  95.  
  96.  
  97.  
  98. //    Initialize all the private variables
  99.  
  100. void gameplayInit(void)
  101. {
  102. int        i,j;
  103.  
  104.     //    precalculate all the trig stuff
  105.     
  106.     for (i=0;i<=NUMANGLES;i++)
  107.     {
  108.         xVertexTable[i][0] = (short) (SIDELENGTH * cosd(pi * (i*REALANGLEDELTA) / 180.0));
  109.         xVertexTable[i][1] = (short) (SIDELENGTH * cosd(pi * (i*REALANGLEDELTA + 135.0) / 180.0));
  110.         xVertexTable[i][2] = (short) (SIDELENGTH * cosd(pi * (i*REALANGLEDELTA + 225.0) / 180.0));
  111.         
  112.         yVertexTable[i][0] = (short) (SIDELENGTH * sind(pi * (i*REALANGLEDELTA) / 180.0));
  113.         yVertexTable[i][1] = (short) (SIDELENGTH * sind(pi * (i*REALANGLEDELTA + 135.0) / 180.0));
  114.         yVertexTable[i][2] = (short) (SIDELENGTH * sind(pi * (i*REALANGLEDELTA + 225.0) / 180.0));
  115.         
  116.         vxTable[i] = THRUST * cosd(pi * (i*REALANGLEDELTA) / 180.0);
  117.         vyTable[i] = THRUST * sind(pi * (i*REALANGLEDELTA) / 180.0);
  118.         
  119.         photonVXTable[i] = MAXSPEED * cosd(pi * (i*REALANGLEDELTA) / 180.0);
  120.         photonVYTable[i] = MAXSPEED * sind(pi * (i*REALANGLEDELTA) / 180.0);
  121.     }
  122.     
  123.     
  124.     //    initialize all the state variables for other players
  125.     
  126.     for (i=0;i<MAXNUMPLAYERS;i++)
  127.     {
  128.         playerXCoord[i] = 0;
  129.         playerYCoord[i] = 0;
  130.         playerAngleIndex[i] = 0;
  131.         
  132.         playerLastXCoord[i] = 0;
  133.         playerLastYCoord[i] = 0;
  134.         playerLastAngleIndex[i] = 0;
  135.         
  136.         playerStatus[i] = IDLE;
  137.         
  138.         for (j=0;j<MAXNUMPHOTONS;j++)
  139.         {
  140.             photonXCoord[i][j] = 0;
  141.             photonYCoord[i][j] = 0;
  142.             
  143.             photonTimeToLive[i][j] = 0;
  144.             
  145.             myPhotonXCoords[j] = 0;
  146.             myPhotonYCoords[j] = 0;
  147.             myPhotonVXs[j] = 0;
  148.             myPhotonVYs[j] = 0;
  149.         }
  150.     }
  151.  
  152.  
  153.     photonIndex = 0;
  154.     
  155.     gameWPtr = nil;
  156.     gameMasterHiddenGWorld = nil;
  157.     gameTempHiddenGWorld = nil;
  158.     
  159.     gameExplosionTimeToLive = 0;
  160.     
  161.     gameNumPlayers = 1;
  162.     
  163.     playerStatus[LOCALPLAYER] = PLAYING;
  164.     
  165.     sendBroadcastUpdate = true;
  166.     dataBroadcastIsComplete = true;
  167.     
  168.     startExplosionSND = false;
  169.     
  170.     gameMyPhotonSNDHandle = nil;
  171.     gameEnemyPhotonSNDHandle = nil;
  172.     gameExplosionSNDHandle = nil;
  173.     
  174.     gameExplosionSndChannelPtr = nil;
  175.     
  176.     for (i=0;i<MAXNUMCHANNELS;i++)
  177.     {
  178.         gameSndChannelPtr[i] = nil;
  179.     }
  180. }
  181.  
  182.  
  183.  
  184. //    Attempt to startup game module-- open windows, allocate sound channels, etc.
  185.  
  186. void gameplayStartup(void)
  187. {
  188. GDHandle        currDevice;
  189. CGrafPtr        currPort;
  190. OSErr            errCode;
  191. PixMapHandle    pixBase;
  192.  
  193.     gameWPtr = (CWindowPtr) GetNewCWindow(GAMEWIND,&gameWRecord,(WindowPtr) -1L);
  194.     
  195.     if (gameWPtr == nil)
  196.     {
  197.         FatalError("Can't allocate memory for game window!");
  198.     }
  199.     
  200.     SetPort((GrafPtr) gameWPtr);
  201.     
  202.     CenterWindow((WindowPtr) gameWPtr);
  203.     
  204.     ShowWindow((WindowPtr) gameWPtr);
  205.     
  206.     gameRect = gameWPtr->portRect;
  207.     
  208.     
  209.     BackPat(&(qd.black));
  210.     EraseRect(&gameRect);
  211.     
  212.     
  213.     GetGWorld(&currPort,&currDevice);
  214.     
  215.     errCode = NewGWorld(&gameMasterHiddenGWorld,0,&gameRect,0L,0L,0);
  216.     
  217.     if (errCode != noErr)
  218.     {
  219.         FatalError("Can't allocate memory for master offscreen GWorld!");
  220.     }
  221.     
  222.     
  223.     errCode = NewGWorld(&gameTempHiddenGWorld,0,&gameRect,0L,0L,0);
  224.     
  225.     if (errCode != noErr)
  226.     {
  227.         FatalError("Can't allocate memory for temporary offscreen GWorld!");
  228.     }
  229.     
  230.     
  231.     midx = gameRect.left + (gameRect.right - gameRect.left) / 2;
  232.     midy = gameRect.top + (gameRect.bottom - gameRect.top) / 2;
  233.     
  234.     sunRect.left = midx - SUNRADIUS;
  235.     sunRect.right = midx + SUNRADIUS;
  236.     sunRect.top = midy - SUNRADIUS;
  237.     sunRect.bottom = midy + SUNRADIUS;
  238.     
  239.  
  240.     myXCoord =  gameRect.left + (gameRect.right - gameRect.left) / 4;
  241.     myYCoord = gameRect.top + (gameRect.bottom - gameRect.top) / 4;
  242.     
  243.     myVX = 0;
  244.     myVY = 0;
  245.     
  246.     playerXCoord[LOCALPLAYER] = myXCoord;
  247.     playerYCoord[LOCALPLAYER] = myYCoord;
  248.     playerAngleIndex[LOCALPLAYER] = MINANGLEINDEX;
  249.     
  250.     playerLastXCoord[LOCALPLAYER] = playerXCoord[LOCALPLAYER];
  251.     playerLastYCoord[LOCALPLAYER] = playerYCoord[LOCALPLAYER];
  252.     playerLastAngleIndex[LOCALPLAYER] = playerAngleIndex[LOCALPLAYER];
  253.     
  254.     ObscureCursor();
  255.     
  256.     DrawSunAndStars();
  257.     
  258.     //    offscreen bitmaps make for smooth animation!
  259.     
  260.     pixBase = GetGWorldPixMap(gameMasterHiddenGWorld);
  261.     
  262.     if (pixBase)
  263.     {
  264.         LockPixels(pixBase);
  265.         
  266.         CopyBits(&(((GrafPtr) gameWPtr)->portBits),(BitMapPtr) *pixBase,&gameRect,&gameRect,srcCopy,nil);
  267.         
  268.         UnlockPixels(pixBase);
  269.     }
  270.     
  271.     
  272.     pixBase = GetGWorldPixMap(gameTempHiddenGWorld);
  273.     
  274.     if (pixBase != nil)
  275.     {
  276.         LockPixels(pixBase);
  277.         
  278.         CopyBits(&(((GrafPtr) gameWPtr)->portBits),(BitMapPtr) *pixBase,&gameRect,&gameRect,srcCopy,nil);
  279.         
  280.         UnlockPixels(pixBase);
  281.     }
  282.     
  283.     
  284.     errCode = SndNewChannel(&gameExplosionSndChannelPtr,0,0L,0L);
  285.     
  286.     if (errCode != noErr || gameExplosionSndChannelPtr == nil)
  287.     {
  288.         gameExplosionSndChannelPtr = nil;
  289.         
  290.         FatalError("Can't open a sound channel!");
  291.     }
  292.  
  293.     
  294.     gameMyPhotonSNDHandle = GetNamedResource('snd ',"\pMy Photon");
  295.     
  296.     gameEnemyPhotonSNDHandle = GetNamedResource('snd ',"\pEnemy Photon");
  297.     
  298.     gameExplosionSNDHandle = GetNamedResource('snd ',"\pExplosion");
  299. }
  300.  
  301.  
  302.  
  303.  
  304. //    Shutdown handler-- close windows, release sound channels, etc.
  305.  
  306. void gameplayShutdown(void)
  307. {
  308. int        i;
  309.  
  310.  
  311.     if (gameMasterHiddenGWorld)
  312.     {
  313.         DisposeGWorld(gameMasterHiddenGWorld);
  314.         
  315.         gameMasterHiddenGWorld = nil;
  316.     }
  317.     
  318.     if (gameTempHiddenGWorld)
  319.     {
  320.         DisposeGWorld(gameTempHiddenGWorld);
  321.         
  322.         gameTempHiddenGWorld = nil;
  323.     }
  324.     
  325.     if (gameWPtr)
  326.     {
  327.         DisposeWindow((WindowPtr) gameWPtr);
  328.         
  329.         gameWPtr = nil;
  330.     }
  331.     
  332.     if (gameMyPhotonSNDHandle)
  333.     {
  334.         ReleaseResource(gameMyPhotonSNDHandle);
  335.         
  336.         gameMyPhotonSNDHandle = 0L;
  337.     }
  338.     
  339.     if (gameEnemyPhotonSNDHandle)
  340.     {
  341.         ReleaseResource(gameEnemyPhotonSNDHandle);
  342.         
  343.         gameEnemyPhotonSNDHandle = 0L;
  344.     }
  345.     
  346.     if (gameExplosionSNDHandle)
  347.     {
  348.         ReleaseResource(gameExplosionSNDHandle);
  349.         
  350.         gameExplosionSNDHandle = 0L;
  351.     }
  352.     
  353.     
  354.     if (gameExplosionSndChannelPtr)
  355.     {
  356.         SndDisposeChannel(gameExplosionSndChannelPtr,true);
  357.         
  358.         gameExplosionSndChannelPtr = 0L;
  359.     }
  360.     
  361.     
  362.     for (i=0;i<MAXNUMCHANNELS;i++)
  363.     {
  364.         if (gameSndChannelPtr[i])
  365.         {
  366.             SndDisposeChannel(gameSndChannelPtr[i],true);
  367.             
  368.             gameSndChannelPtr[i] = 0L;
  369.         }
  370.     }
  371. }
  372.  
  373.  
  374.  
  375.  
  376. //    Nothing much to do for window activation right now....
  377.  
  378. void gameplayActivateWindow(WindowPtr theWPtr,char adFlag)
  379. {
  380.  
  381. }
  382.  
  383.  
  384.  
  385. //    Nothing much to do for mouseclicks in the window either....
  386.  
  387. void gameplayDoContent(WindowPtr theWPtr,EventRecord *theEvent)
  388. {
  389. GrafPtr            oldPort;
  390.  
  391.     if (gameWPtr != nil && theWPtr == (WindowPtr) gameWPtr)
  392.     {
  393.         GetPort(&oldPort);
  394.         SetPort((GrafPtr) gameWPtr);
  395.         GlobalToLocal(&theEvent->where);
  396.         
  397.         SetPort(oldPort);
  398.     }
  399. }
  400.  
  401.  
  402.  
  403.  
  404. //    The windows.c code calls this to check to see if an event applies to one of our windows.
  405.  
  406. OSErr gameplayIsWindow(WindowPtr theWPtr)
  407. {
  408.     if (gameWPtr != nil && theWPtr == (WindowPtr) gameWPtr)
  409.     {
  410.         return(noErr);
  411.     }
  412.     
  413.     else
  414.     {
  415.         return(BADWINDOW);
  416.     }
  417. }
  418.  
  419.  
  420.  
  421.  
  422. //    Update the on-screen display by copying the image from the offscreen bitmap.
  423.  
  424. void gameplayUpdateWindow(WindowPtr theWPtr)
  425. {
  426. GrafPtr            oldPort;
  427. PixMapHandle    pixBase;
  428.  
  429.  
  430.     if (gameWPtr != nil && theWPtr == (WindowPtr) gameWPtr)
  431.     {
  432.         GetPort(&oldPort);
  433.         SetPort((GrafPtr) gameWPtr);
  434.         
  435.         BeginUpdate((WindowPtr) gameWPtr);
  436.         
  437.         pixBase = GetGWorldPixMap(gameMasterHiddenGWorld);
  438.         
  439.         if (pixBase)
  440.         {
  441.             LockPixels(pixBase);
  442.             
  443.             CopyBits((BitMapPtr) *pixBase,&(((GrafPtr) gameWPtr)->portBits),&gameRect,&gameRect,srcCopy,nil);
  444.             
  445.             UnlockPixels(pixBase);
  446.         }
  447.         
  448.         EndUpdate((WindowPtr) gameWPtr);
  449.         
  450.         SetPort(oldPort);
  451.     }
  452. }
  453.  
  454.  
  455.  
  456. //    Let the user drag the window around.
  457.  
  458. void gameplayDragWindow(WindowPtr theWPtr,EventRecord *theEvent)
  459. {
  460. Rect    tempRect;
  461.  
  462.     if (gameWPtr != nil && theWPtr == (WindowPtr) gameWPtr)
  463.     {
  464.         getGrayRgnRect(&tempRect);
  465.  
  466.         DragWindow((WindowPtr) gameWPtr,theEvent->where,&tempRect);
  467.     }
  468. }
  469.  
  470.  
  471.  
  472.  
  473. //    Draw the sun and the stars so they can be copied to the offscreen bitmap.
  474.  
  475. static void DrawSunAndStars(void)
  476. {
  477. int        i;
  478. short    tempX,tempY;
  479.  
  480.  
  481.     SetPort((GrafPtr) gameWPtr);
  482.     
  483.     BackPat(&(qd.black));
  484.     EraseRect(&gameRect);
  485.     
  486.     
  487.     PenNormal();
  488.     
  489.     FillOval(&sunRect,&(qd.white));
  490.     
  491.     GetDateTime((unsigned long *) &(qd.randSeed));
  492.     
  493.     PenPat(&(qd.white));
  494.     
  495.     for (i=0;i<NUMSTARS;i++)
  496.     {
  497.         tempX = gameRect.left + ((Random() & 0x7FFF) % (gameRect.right - gameRect.left));
  498.         tempY = gameRect.top + ((Random() & 0x7FFF) % (gameRect.bottom - gameRect.top));
  499.         
  500.         MoveTo(tempX,tempY);
  501.         Line(0,0);
  502.     }
  503. }
  504.  
  505.  
  506.  
  507.  
  508.  
  509.  
  510. //    Play a sound if a sound channel has been allocated and one is available.
  511.  
  512. static void PlayGameSound(Handle theSNDHandle)
  513. {
  514. int            i,errCode;
  515. SCStatus    theStatus;
  516.  
  517.     
  518.     if (theSNDHandle == nil)
  519.     {
  520.         goto EXITPOINT;
  521.     }
  522.     
  523.     
  524.     for (i=0;i < MAXNUMCHANNELS;i++)
  525.     {
  526.         if (gameSndChannelPtr[i] == 0L)
  527.         {
  528.             errCode = SndNewChannel(&(gameSndChannelPtr[i]),0,0L,0L);
  529.             
  530.             if (errCode != noErr)
  531.             {
  532.                 gameSndChannelPtr[i] = 0L;
  533.             }
  534.         }
  535.         
  536.         
  537.         if (gameSndChannelPtr[i])
  538.         {
  539.             errCode = SndChannelStatus(gameSndChannelPtr[i],sizeof(theStatus),&theStatus);
  540.             
  541.             if (errCode == noErr && theStatus.scChannelBusy == false)
  542.             {
  543.                 errCode = SndPlay(gameSndChannelPtr[i],(SndListHandle) theSNDHandle,true);
  544.                 
  545.                 goto EXITPOINT;
  546.             }
  547.         }
  548.     }
  549.     
  550. EXITPOINT:
  551.  
  552.     return;
  553. }
  554.  
  555.  
  556.  
  557. //    Erase the image of a ship in the offscreen bitmap.
  558.  
  559. static void EraseOffscreenShip(int whichShip)
  560. {
  561. short            lastX,lastY;
  562. Rect            updateRect;
  563. PixMapHandle    masterPixBase,tempPixBase;
  564.  
  565.     
  566.     lastX = (short) playerLastXCoord[whichShip];
  567.     lastY = (short) playerLastYCoord[whichShip];
  568.     
  569.     updateRect.left = lastX - SIDELENGTH - 1;
  570.     updateRect.right = lastX + SIDELENGTH + 1;
  571.     updateRect.top = lastY - SIDELENGTH - 1;
  572.     updateRect.bottom = lastY + SIDELENGTH + 1;
  573.         
  574.     SectRect(&updateRect,&gameRect,&updateRect);
  575.     
  576.     //    Copy the "blank" image (sun and stars only) over top of the players location
  577.     
  578.     masterPixBase = GetGWorldPixMap(gameMasterHiddenGWorld);
  579.     tempPixBase = GetGWorldPixMap(gameTempHiddenGWorld);
  580.     
  581.     if (masterPixBase != nil && tempPixBase != nil)
  582.     {
  583.         LockPixels(masterPixBase);
  584.         LockPixels(tempPixBase);
  585.         
  586.         CopyBits((BitMapPtr) *masterPixBase,(BitMapPtr) *tempPixBase,&updateRect,&updateRect,srcCopy,nil);
  587.         
  588.         UnlockPixels(masterPixBase);
  589.         UnlockPixels(tempPixBase);
  590.     }
  591. }
  592.  
  593.  
  594.  
  595. //    Draw a spaceship offscreen.
  596.  
  597. static void DrawOffscreenShip(int whichShip)
  598. {
  599. short            centerX,centerY,angle,x1,y1,x2,y2,x3,y3;
  600. PixMapHandle    tempPixBase;
  601.  
  602.     
  603.     centerX = (short) playerXCoord[whichShip];
  604.     centerY = (short) playerYCoord[whichShip];
  605.     angle = (short) playerAngleIndex[whichShip];
  606.     
  607.     x1 = centerX + xVertexTable[playerAngleIndex[whichShip]][0];
  608.     x2 = centerX + xVertexTable[playerAngleIndex[whichShip]][1];
  609.     x3 = centerX + xVertexTable[playerAngleIndex[whichShip]][2];
  610.     
  611.     y1 = centerY + yVertexTable[playerAngleIndex[whichShip]][0];
  612.     y2 = centerY + yVertexTable[playerAngleIndex[whichShip]][1];
  613.     y3 = centerY + yVertexTable[playerAngleIndex[whichShip]][2];
  614.     
  615.     SetPort((GrafPtr) gameTempHiddenGWorld);
  616.  
  617.     PenNormal();
  618.     PenPat(&(qd.white));
  619.  
  620.     MoveTo(x1,y1);
  621.     LineTo(x2,y2);
  622.     LineTo(centerX,centerY);
  623.     LineTo(x3,y3);
  624.     LineTo(x1,y1);
  625.     
  626.     //    The local player looks a little different
  627.     
  628.     if (whichShip == LOCALPLAYER)
  629.     {
  630.         LineTo(centerX,centerY);
  631.     }
  632.     
  633.     //    since broadcast data arrives asynchronously, the location of the player could change while we're in the middle of drawing him,
  634.     //    so we'd better keep track of where we think he is now
  635.     
  636.     playerTempCurrentX[whichShip] = centerX;
  637.     playerTempCurrentY[whichShip] = centerY;
  638.     playerTempCurrentAngle[whichShip] = angle;
  639. }
  640.  
  641.  
  642.  
  643.  
  644. //    Copy an offscreen ship onscreen where we can see it.
  645.  
  646. static void ShowShip(int whichShip)
  647. {
  648. short            centerX,centerY,angle,lastX,lastY;
  649. Rect            updateRect;
  650. PixMapHandle    tempPixBase;
  651.  
  652.     
  653.     //    remember that broadcast data arrives asynchronously, so use some dependable variables that don't change unexpectedly....
  654.     
  655.     centerX = (short) playerTempCurrentX[whichShip];
  656.     centerY = (short) playerTempCurrentY[whichShip];
  657.     angle = playerTempCurrentAngle[whichShip];
  658.     
  659.     lastX = (short) playerLastXCoord[whichShip];
  660.     lastY = (short) playerLastYCoord[whichShip];
  661.     
  662.     
  663.     if (centerX < lastX)
  664.     {
  665.         updateRect.left = centerX - SIDELENGTH - 1;
  666.         updateRect.right = lastX + SIDELENGTH + 1;
  667.     }
  668.     else
  669.     {
  670.         updateRect.left = lastX - SIDELENGTH - 1;
  671.         updateRect.right = centerX + SIDELENGTH + 1;
  672.     }
  673.     
  674.     
  675.     if (centerY < lastY)
  676.     {
  677.         updateRect.top = centerY - SIDELENGTH - 1;
  678.         updateRect.bottom = lastY + SIDELENGTH + 1;
  679.     }
  680.     else
  681.     {
  682.         updateRect.top = lastY - SIDELENGTH - 1;
  683.         updateRect.bottom = centerY + SIDELENGTH + 1;
  684.     }
  685.  
  686.     
  687.     SectRect(&updateRect,&gameRect,&updateRect);
  688.     
  689.     
  690.     tempPixBase = GetGWorldPixMap(gameTempHiddenGWorld);
  691.     
  692.     if (tempPixBase != nil)
  693.     {
  694.         LockPixels(tempPixBase);
  695.         
  696.         CopyBits((BitMapPtr) *tempPixBase,&(((GrafPtr) gameWPtr)->portBits),&updateRect,&updateRect,srcCopy,nil);
  697.         
  698.         UnlockPixels(tempPixBase);
  699.     }
  700.     
  701.     
  702.     //    if our position has changed since the last redraw, we need to broadcast our new position
  703.     
  704.     if (whichShip == LOCALPLAYER && (playerLastXCoord[whichShip] != centerX || playerLastYCoord[whichShip] != centerY))
  705.     {
  706.         sendBroadcastUpdate = true;
  707.     }
  708.     
  709.     playerLastXCoord[whichShip] = centerX;
  710.     playerLastYCoord[whichShip] = centerY;
  711.     playerLastAngleIndex[whichShip] = angle;
  712. }
  713.  
  714.  
  715.  
  716. //    XOR the photons to make them invisible again
  717.  
  718. static void ErasePhotons(void)
  719. {
  720. int        i,j;
  721. short    x,y;
  722.  
  723.     SetPort((GrafPtr) gameWPtr);
  724.     
  725.     PenNormal();
  726.     
  727.     PenMode(patXor);
  728.     
  729.     for (i=0;i<gameNumPlayers;i++)
  730.     {
  731.         for (j=0;j<MAXNUMPHOTONS;j++)
  732.         {
  733.             if (photonTimeToLive[i][j] > 0)
  734.             {
  735.                 x = photonLastXCoord[i][j];
  736.                 y = photonLastYCoord[i][j];
  737.                 
  738.                 if (x >= gameRect.left && x < gameRect.right &&
  739.                     y >= gameRect.top && y < gameRect.bottom)
  740.                 {
  741.                     MoveTo(x,y);
  742.                     Line(0,0);
  743.                 }
  744.             }
  745.         }
  746.     }
  747. }
  748.  
  749.  
  750.  
  751. //    XOR the photons to make them visible
  752.  
  753. static void DrawPhotons(void)
  754. {
  755. int            i,j;
  756. short        x,y,midx,midy;
  757. Rect        hitRect;
  758.  
  759.  
  760.     SetPort((GrafPtr) gameWPtr);
  761.     
  762.     PenNormal();
  763.     
  764.     PenMode(patXor);
  765.     
  766.     //    while we're drawing other players' photons, check to see if we've been hit
  767.     
  768.     hitRect.left = playerTempCurrentX[LOCALPLAYER] - (2*SIDELENGTH)/3;
  769.     hitRect.right = playerTempCurrentX[LOCALPLAYER] + (2*SIDELENGTH)/3;
  770.     hitRect.top = playerTempCurrentY[LOCALPLAYER] - (2*SIDELENGTH)/3;
  771.     hitRect.bottom = playerTempCurrentY[LOCALPLAYER] + (2*SIDELENGTH)/3;
  772.     
  773.     for (i=0;i<gameNumPlayers;i++)
  774.     {
  775.         for (j=0;j<MAXNUMPHOTONS;j++)
  776.         {
  777.             if (photonTimeToLive[i][j] > 0)
  778.             {
  779.                 x = photonXCoord[i][j];
  780.                 y = photonYCoord[i][j];
  781.                 
  782.                 if (x >= gameRect.left && x < gameRect.right &&
  783.                     y >= gameRect.top && y < gameRect.bottom)
  784.                 {
  785.                     MoveTo(x,y);
  786.                     Line(0,0);
  787.                 }
  788.                 
  789.                 
  790.                 if (playerStatus[LOCALPLAYER] == PLAYING)
  791.                 {
  792.                     //    have we been hit?
  793.                     
  794.                     if (x >= hitRect.left && x <= hitRect.right && y >= hitRect.top && y <= hitRect.bottom)
  795.                     {
  796.                         playerStatus[LOCALPLAYER] = EXPLODING;
  797.                     }
  798.                     
  799.                     //    check halfway between current location and previous location, too
  800.                     
  801.                     else
  802.                     {
  803.                         midx = (x + photonLastXCoord[i][j]) / 2;
  804.                         midy = (y + photonLastYCoord[i][j]) / 2;
  805.                         
  806.                         if (midx >= hitRect.left && midx <= hitRect.right && midy >= hitRect.top && midy <= hitRect.bottom)
  807.                         {
  808.                             playerStatus[LOCALPLAYER] = EXPLODING;
  809.                         }
  810.                     }
  811.                     
  812.                     
  813.                     if (playerStatus[LOCALPLAYER] == EXPLODING)
  814.                     {
  815.                         gameExplosionTimeToLive = EXPLOSIONTTL;
  816.                         
  817.                         startExplosionSND = true;
  818.                     }
  819.                 }
  820.                 
  821.                 photonLastXCoord[i][j] = x;
  822.                 photonLastYCoord[i][j] = y;
  823.             }
  824.             
  825.             
  826.             if (i != LOCALPLAYER && photonLastTimeToLive[i][j] < photonTimeToLive[i][j])
  827.             {
  828.                 PlayGameSound(gameEnemyPhotonSNDHandle);
  829.             }
  830.             
  831.             
  832.             photonLastTimeToLive[i][j] = photonTimeToLive[i][j];
  833.         }
  834.     }
  835. }
  836.  
  837.  
  838.  
  839.  
  840. //    This routine gets called when a broadcast packet has been sent by the serialSendData() or appleTalkingSendData() routines
  841.  
  842. void gameplayHandleBroadcastCompletion(void)
  843. {
  844.     dataBroadcastIsComplete = true;
  845. }
  846.  
  847.  
  848.  
  849. //    Attempt to broadcast a packet containing our current position, angle, photon positions....
  850.  
  851. static void BroadcastLocalPlayerData(void)
  852. {
  853. int                    i;
  854. GameData            theBroadcastData;
  855.  
  856.     theBroadcastData.x = playerXCoord[LOCALPLAYER];
  857.     theBroadcastData.y = playerYCoord[LOCALPLAYER];
  858.     theBroadcastData.angleIndex = playerAngleIndex[LOCALPLAYER];
  859.     
  860.     for (i=0;i<MAXNUMPHOTONS;i++)
  861.     {
  862.         theBroadcastData.photonsX[i] = photonXCoord[LOCALPLAYER][i];
  863.         theBroadcastData.photonsY[i] = photonYCoord[LOCALPLAYER][i];
  864.         theBroadcastData.photonsTimeToLive[i] = photonTimeToLive[LOCALPLAYER][i];
  865.     }
  866.     
  867.     theBroadcastData.playerStatus = playerStatus[LOCALPLAYER];
  868.     
  869.     
  870.     if (connectionMethod == APPLETALK)
  871.     {
  872.         appleTalkingSendData((Ptr) &theBroadcastData,sizeof(GameData));
  873.     }
  874.     
  875.     else if (connectionMethod == SERIAL)
  876.     {
  877.         serialSendData((Ptr) &theBroadcastData,sizeof(GameData));
  878.     }
  879. }
  880.  
  881.  
  882.  
  883. //    This routine handles incoming broadcast data from other players.
  884.  
  885. void gameReceiveBroadcastData(Ptr theBroadcastData,long theBroadcastDataLength)
  886. {
  887. int            i,j;
  888. char        found;
  889. GameData     *thePacket;
  890.  
  891.     
  892.     //    if we're AppleTalked, then we have to deal with data from many other players
  893.     
  894.     if (connectionMethod == APPLETALK)
  895.     {
  896.         found = false;
  897.         
  898.         for (i=1;i<gameNumPlayers;i++)
  899.         {
  900.             //    Find out which player it came from
  901.             
  902.             if (gamePlayerAddresses[i].aNode == (*((DDPDataPacket *) theBroadcastData)).srcNodeID  && gamePlayerAddresses[i].aSocket == (*((DDPDataPacket *) theBroadcastData)).srcSocketNumber)
  903.             {
  904.                 thePacket = (GameData *) &((*((DDPDataPacket *) theBroadcastData)).packetData);
  905.                 
  906.                 playerXCoord[i] = (*thePacket).x;
  907.                 playerYCoord[i] = (*thePacket).y;
  908.                 playerAngleIndex[i] = (*thePacket).angleIndex;
  909.                 
  910.                 for (j=0;j<MAXNUMPHOTONS;j++)
  911.                 {
  912.                     photonXCoord[i][j] = (*thePacket).photonsX[j];
  913.                     photonYCoord[i][j] = (*thePacket).photonsY[j];
  914.                     photonTimeToLive[i][j] = (*thePacket).photonsTimeToLive[j];
  915.                 }        
  916.                 
  917.                 if (playerStatus[i] != EXPLODING && (*thePacket).playerStatus == EXPLODING)
  918.                 {
  919.                     startExplosionSND = true;
  920.                 }
  921.                 
  922.                 playerStatus[i] = (*thePacket).playerStatus;
  923.                 
  924.                 found = true;
  925.                 
  926.                 break;
  927.             }
  928.         }
  929.         
  930.         
  931.         //    if it's someone we don't, then add them to our list of known players
  932.         
  933.         if (found == false && gameNumPlayers < MAXNUMPLAYERS)
  934.         {
  935.             gamePlayerAddresses[gameNumPlayers].aNode = (*((DDPDataPacket *) theBroadcastData)).srcNodeID;
  936.             gamePlayerAddresses[gameNumPlayers].aSocket = (*((DDPDataPacket *) theBroadcastData)).srcSocketNumber;
  937.             
  938.             thePacket = (GameData *) &((*((DDPDataPacket *) theBroadcastData)).packetData);
  939.             
  940.             playerXCoord[gameNumPlayers] = (*thePacket).x;
  941.             playerYCoord[gameNumPlayers] = (*thePacket).y;
  942.             playerAngleIndex[gameNumPlayers] = (*thePacket).angleIndex;
  943.             
  944.             for (j=0;j<MAXNUMPHOTONS;j++)
  945.             {
  946.                 photonXCoord[gameNumPlayers][j] = (*thePacket).photonsX[j];
  947.                 photonYCoord[gameNumPlayers][j] = (*thePacket).photonsY[j];
  948.                 photonTimeToLive[gameNumPlayers][j] = (*thePacket).photonsTimeToLive[j];
  949.                 photonLastTimeToLive[gameNumPlayers][j] = 0;
  950.             }
  951.             
  952.             playerStatus[gameNumPlayers] = (*thePacket).playerStatus;
  953.             
  954.             
  955.             playerLastXCoord[gameNumPlayers] = playerXCoord[gameNumPlayers];
  956.             playerLastYCoord[gameNumPlayers] = playerYCoord[gameNumPlayers];
  957.             playerLastAngleIndex[gameNumPlayers] = playerAngleIndex[gameNumPlayers];
  958.         
  959.             gameNumPlayers++;
  960.         }
  961.     }
  962.     
  963.     //    if we're connected via serial cable, then there can be only one other player
  964.     
  965.     else if (connectionMethod == SERIAL)
  966.     {
  967.         gameNumPlayers = 2;
  968.         
  969.         thePacket = (GameData *) theBroadcastData;
  970.         
  971.         playerXCoord[1] = (*thePacket).x;
  972.         playerYCoord[1] = (*thePacket).y;
  973.         playerAngleIndex[1] = (*thePacket).angleIndex;
  974.         
  975.         for (j=0;j<MAXNUMPHOTONS;j++)
  976.         {
  977.             photonXCoord[1][j] = (*thePacket).photonsX[j];
  978.             photonYCoord[1][j] = (*thePacket).photonsY[j];
  979.             photonTimeToLive[1][j] = (*thePacket).photonsTimeToLive[j];
  980.         }        
  981.         
  982.         if (playerStatus[1] != EXPLODING && (*thePacket).playerStatus == EXPLODING)
  983.         {
  984.             startExplosionSND = true;
  985.         }
  986.         
  987.         playerStatus[1] = (*thePacket).playerStatus;
  988.     }
  989. }
  990.  
  991.  
  992.  
  993.  
  994. //    This routine does most of the work of updating our position and figuring out when screen redraws should occur.
  995.  
  996. void gameplayIdle(void)
  997. {
  998. KeyMap        theKeyMap;
  999. int            i,currentNumPlayers;
  1000. short        x,y,dx,dy;
  1001. SCStatus    theStatus;
  1002. OSErr        errCode;
  1003. double        ax,ay,rSquared,r,dt;
  1004. static unsigned long        thisTicks,lastTicks = 0,deltaTicks;
  1005. static unsigned long        totalTicks = 0,numTimes = 0;
  1006.  
  1007.     //    try to figure out how much time has elapsed since the last time we did this
  1008.     
  1009.     thisTicks = TickCount();
  1010.     
  1011.     if (lastTicks != 0)
  1012.     {
  1013.         deltaTicks = thisTicks - lastTicks;
  1014.     }
  1015.     
  1016.     lastTicks = thisTicks;
  1017.     
  1018.     totalTicks += deltaTicks;
  1019.     numTimes++;    // this isn't necessary, but I used it to help generate some stats about the speed of the code
  1020.  
  1021.     currentNumPlayers = gameNumPlayers;
  1022.     
  1023.     for (i=0;i<currentNumPlayers;i++)
  1024.     {
  1025.         EraseOffscreenShip(i);
  1026.     }
  1027.     
  1028.  
  1029.     //    update our position no more than 60 times per second (be nice to the slow machines we're playing against)
  1030.         
  1031.     if (deltaTicks > 0)
  1032.     {
  1033.         dt = deltaTicks;
  1034.         
  1035.         // don't you wish you'd paid attention in highschool when you did Pythagorean Theorem and Newton's Law of Gravitation?
  1036.         
  1037.         rSquared = (myXCoord - midx)*(myXCoord - midx) + (myYCoord - midy)*(myYCoord - midy);
  1038.         r = sqrtd(rSquared);
  1039.         
  1040.         if (fabsd(r) > SUNRADIUS)
  1041.         {
  1042.             ax = ((myXCoord - midx) / r) * (GRAVITY / rSquared);
  1043.             ay = ((myYCoord - midy) / r) * (GRAVITY / rSquared);
  1044.         }
  1045.         
  1046.         else
  1047.         {
  1048.             ax = 0.0;
  1049.             ay = 0.0;
  1050.         }
  1051.         
  1052.         
  1053.         myVX += ax*dt;
  1054.         myVY += ay*dt;
  1055.     
  1056.         
  1057.         //    If we've been hit, then bounce us around a bit
  1058.         
  1059.         if (playerStatus[LOCALPLAYER] == EXPLODING)
  1060.         {
  1061.             if (gameExplosionTimeToLive > 0)
  1062.             {
  1063.                 gameExplosionTimeToLive--;
  1064.             }
  1065.             
  1066.             else
  1067.             {
  1068.                 playerStatus[LOCALPLAYER] = PLAYING;
  1069.             }
  1070.             
  1071.             
  1072.             dx = ((Random() & 0x0003) - 2);
  1073.             
  1074.             if (dx == 0)
  1075.             {
  1076.                 dx = 2;
  1077.             }
  1078.             
  1079.             dy = ((Random() & 0x0003) - 2);
  1080.             
  1081.             if (dy == 0)
  1082.             {
  1083.                 dy = 2;
  1084.             }    
  1085.             
  1086.             myXCoord += dx;
  1087.             myYCoord += dy;
  1088.         }
  1089.         
  1090.         
  1091.         myVX *= 0.995;
  1092.         myVY *= 0.995;
  1093.         
  1094.         if (myVX > MAXSPEED)
  1095.             myVX = MAXSPEED;
  1096.         else if (myVX < -MAXSPEED)
  1097.             myVX = -MAXSPEED;
  1098.         
  1099.         if (myVY > MAXSPEED)
  1100.             myVY = MAXSPEED;
  1101.         else if (myVY < -MAXSPEED)
  1102.             myVY = -MAXSPEED;
  1103.         
  1104.         myXCoord += myVX*dt;
  1105.         myYCoord += myVY*dt;
  1106.         
  1107.         x = (short) myXCoord;
  1108.         y = (short) myYCoord;
  1109.         
  1110.         
  1111.         //    did we bump the edge of the screen?
  1112.         
  1113.         if (x < gameRect.left)
  1114.         {
  1115.             x = gameRect.left;
  1116.             myXCoord = gameRect.left;
  1117.             myVX = -myVX;
  1118.         }
  1119.         
  1120.         else if (x > gameRect.right)
  1121.         {
  1122.             x = gameRect.right;
  1123.             myXCoord = gameRect.right;
  1124.             myVX = -myVX;
  1125.         }
  1126.         
  1127.         
  1128.         if (y < gameRect.top)
  1129.         {
  1130.             y = gameRect.top;
  1131.             myYCoord = gameRect.top;
  1132.             myVY = -myVY;
  1133.         }
  1134.         
  1135.         else if (y > gameRect.bottom)
  1136.         {
  1137.             y = gameRect.bottom;
  1138.             myYCoord = gameRect.bottom;
  1139.             myVY = -myVY;
  1140.         }
  1141.         
  1142.         
  1143.         playerXCoord[LOCALPLAYER] = x;
  1144.         playerYCoord[LOCALPLAYER] = y;
  1145.     }
  1146.     
  1147.     else
  1148.     {
  1149.         x = (short) myXCoord;
  1150.         y = (short) myYCoord;
  1151.     }
  1152.     
  1153.     
  1154.     //    figure out which (if any) keys have been pressed
  1155.     
  1156.     GetKeys(theKeyMap);
  1157.     
  1158.     if (theKeyMap[3] & LEFTARROWKEYMAPMASK)
  1159.     {
  1160.         playerAngleIndex[LOCALPLAYER] -= deltaTicks;
  1161.         
  1162.         if (playerAngleIndex[LOCALPLAYER] <= MINANGLEINDEX)
  1163.         {
  1164.             playerAngleIndex[LOCALPLAYER] += MAXANGLEINDEX;
  1165.         }
  1166.         
  1167.         sendBroadcastUpdate = true;
  1168.     }
  1169.     
  1170.     if (theKeyMap[3] & RIGHTARROWKEYMAPMASK)
  1171.     {
  1172.         playerAngleIndex[LOCALPLAYER] += deltaTicks;
  1173.         
  1174.         if (playerAngleIndex[LOCALPLAYER] >= MAXANGLEINDEX)
  1175.         {
  1176.             playerAngleIndex[LOCALPLAYER] -= MAXANGLEINDEX;
  1177.         }
  1178.         
  1179.         sendBroadcastUpdate = true;
  1180.     }
  1181.     
  1182.     
  1183.     //    ahead half-impulse, Mr. Data....
  1184.     
  1185.     if (theKeyMap[3] & UPARROWKEYMAPMASK)
  1186.     {
  1187.         myVX += vxTable[playerAngleIndex[LOCALPLAYER]];
  1188.         myVY += vyTable[playerAngleIndex[LOCALPLAYER]];
  1189.     }
  1190.     
  1191.     
  1192.     //    update photon positions no more than 60 times per second
  1193.     
  1194.     if (deltaTicks > 0)
  1195.     {
  1196.         for (i=0;i<MAXNUMPHOTONS;i++)
  1197.         {
  1198.             if (photonTimeToLive[LOCALPLAYER][i] > 0)
  1199.             {
  1200.                 photonTimeToLive[LOCALPLAYER][i] -= deltaTicks;
  1201.                 
  1202.                 if (photonTimeToLive[LOCALPLAYER][i] > 0)
  1203.                 {
  1204.                     myPhotonXCoords[i] += myPhotonVXs[i]*dt;
  1205.                     myPhotonYCoords[i] += myPhotonVYs[i]*dt;
  1206.                     
  1207.                     photonXCoord[LOCALPLAYER][i] = (short) myPhotonXCoords[i];
  1208.                     photonYCoord[LOCALPLAYER][i] = (short) myPhotonYCoords[i];
  1209.                     
  1210.                     sendBroadcastUpdate = true;
  1211.                 }
  1212.             }
  1213.         }
  1214.     }
  1215.     
  1216.     
  1217.     //    don't fire photons *too* fast or we'll burn out the emitter array, Mr. Worf
  1218.     
  1219.     if ((TickCount() > photonLastTickCount + PHOTONFIREDELAY) && theKeyMap[1] & SPACEKEYMAPMASK)
  1220.     {
  1221.         if (photonTimeToLive[LOCALPLAYER][photonIndex] <= 0)
  1222.         {
  1223.             myPhotonXCoords[photonIndex] = myXCoord + xVertexTable[playerAngleIndex[LOCALPLAYER]][0];
  1224.             myPhotonYCoords[photonIndex] = myYCoord + yVertexTable[playerAngleIndex[LOCALPLAYER]][0];
  1225.             
  1226.             photonXCoord[LOCALPLAYER][photonIndex] = (short) myPhotonXCoords[photonIndex];
  1227.             photonYCoord[LOCALPLAYER][photonIndex] = (short) myPhotonYCoords[photonIndex];
  1228.             
  1229.             myPhotonVXs[photonIndex] = photonVXTable[playerAngleIndex[LOCALPLAYER]];
  1230.             myPhotonVYs[photonIndex] = photonVYTable[playerAngleIndex[LOCALPLAYER]];
  1231.             
  1232.             photonTimeToLive[LOCALPLAYER][photonIndex] = PHOTONLIFETIME;
  1233.             
  1234.             SetPort((GrafPtr) gameWPtr);
  1235.             
  1236.             PenNormal();
  1237.             
  1238.             PenMode(patXor);
  1239.             
  1240.             MoveTo((short) photonXCoord[LOCALPLAYER][photonIndex],(short) photonYCoord[LOCALPLAYER][photonIndex]);
  1241.             Line(0,0);
  1242.                     
  1243.             photonIndex++;
  1244.             
  1245.             if (photonIndex >= MAXNUMPHOTONS)
  1246.                 photonIndex = 0;
  1247.             
  1248.             photonLastTickCount = TickCount();
  1249.             
  1250.             PlayGameSound(gameMyPhotonSNDHandle);
  1251.         }
  1252.     }
  1253.         
  1254.     
  1255.     //    redraw everyone offscreen
  1256.     
  1257.     for (i=0;i<currentNumPlayers;i++)
  1258.     {
  1259.         DrawOffscreenShip(i);
  1260.     }
  1261.     
  1262.     
  1263.     //    erase the photons' old positions
  1264.     
  1265.     ErasePhotons();
  1266.     
  1267.     
  1268.     //    show everyone onscreen
  1269.     
  1270.     for (i=0;i<currentNumPlayers;i++)
  1271.     {
  1272.         ShowShip(i);
  1273.     }
  1274.     
  1275.     
  1276.     //    redraw photons at their new positions
  1277.     
  1278.     DrawPhotons();
  1279.     
  1280.     
  1281.     //    should we send a new update of our position?
  1282.     
  1283.     if (sendBroadcastUpdate == true && dataBroadcastIsComplete == true)
  1284.     {
  1285.         sendBroadcastUpdate = false;
  1286.         dataBroadcastIsComplete == false;
  1287.         
  1288.         BroadcastLocalPlayerData();
  1289.     }
  1290.     
  1291.     //    did we just get hit by another player's photon?
  1292.     
  1293.     if (startExplosionSND == true)
  1294.     {
  1295.         startExplosionSND = false;
  1296.         
  1297.         errCode = SndChannelStatus(gameExplosionSndChannelPtr,sizeof(theStatus),&theStatus);
  1298.         
  1299.         if (errCode == noErr && theStatus.scChannelBusy == false)
  1300.         {
  1301.             errCode = SndPlay(gameExplosionSndChannelPtr,(SndListHandle) gameExplosionSNDHandle,true);
  1302.         }
  1303.     }
  1304. }
  1305.  
  1306.  
  1307.  
  1308.  
  1309.  
  1310.